package com.heima.config;

import com.nimbusds.jose.jwk.JWK;
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.JwtEncoder;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.security.oauth2.jwt.NimbusJwtEncoder;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint;
import org.springframework.security.oauth2.server.resource.web.access.BearerTokenAccessDeniedHandler;
import org.springframework.security.web.SecurityFilterChain;

import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;

// 配置，有哪些接口需要进行权限校验
@Configuration(proxyBeanMethods = false)
@EnableMethodSecurity
public class WebSecurityConfigurer {

    // JWT加解密用的公钥
    @Value("${jwt.public.key}")
    private RSAPublicKey key;

    // JWT加解密用的私钥
    @Value("${jwt.private.key}")
    private RSAPrivateKey priv;

    // 在这个方法里面，主要做了几件事
    // 1. 有哪些接口要校验、哪些接口不校验
    // 2. 校验的话，需要用什么方式去校验
    // 3. 校验不通过的话，应该要怎么去处理
    @Bean
    SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
        // 配置哪些地址不需要做权限校验
        http.authorizeRequests().antMatchers("/login").anonymous();
        //http.authorizeRequests().antMatchers("/login").permitAll();

        // 设置校验规则
        // 拦截所有的意思 .anyRequest().authenticated()
        http.authorizeRequests().anyRequest().authenticated()
                .and()
                // 不创建session
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                // 防止CSRF攻击
                .csrf((csrf) -> csrf.ignoringAntMatchers("/login"))
                // JWT处理类 JwtAuthenticationConverter 在下面定义
                .oauth2ResourceServer().jwt().jwtAuthenticationConverter(
                        new JwtAuthenticationConverter()
                );

        // 异常处理类定义
        http.exceptionHandling((exceptions) -> exceptions
                // 匿名无权限类
                .authenticationEntryPoint(new BearerTokenAuthenticationEntryPoint())
                // 无权限的处理方式
                // 处理类可以自己实现，自定义处理类只需要实现 AccessDeniedHandler 即可
                .accessDeniedHandler(new BearerTokenAccessDeniedHandler())
        );

        //支持跨域
        http.cors();

        return http.build();
    }

    /**
     * JWT 解密类，需要设置公钥
     * 使用框架内置的 NimbusJwtDecoder.withPublicKey(this.key).build()
     * @return JwtDecoder
     */
    @Bean
    JwtDecoder jwtDecoder() {
        return NimbusJwtDecoder.withPublicKey(this.key).build();
    }

    /**
     * JWT 加密类，需要设置私钥
     * @return JwtEncoder
     */
    @Bean
    JwtEncoder jwtEncoder() {
        JWK jwk = new RSAKey.Builder(this.key).privateKey(this.priv).build();
        JWKSource<SecurityContext> jwks = new ImmutableJWKSet<>(new JWKSet(jwk));
        return new NimbusJwtEncoder(jwks);
    }

    @Bean
    JwtAuthenticationConverter JwtAuthenticationConverter() {
        final JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
        //jwtGrantedAuthoritiesConverter.setAuthorityPrefix(" ");
        final JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
        jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(jwtGrantedAuthoritiesConverter);
        return jwtAuthenticationConverter;
    }
}
